using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using OpenComposite.Base.Collections;
using OpenComposite.EII.Forms;
using OpenComposite.EII.Repository;
using OpenComposite.Mapper;

namespace OpenComposite.EII.Designer.SCA
{
	public partial class FormComponentBinding : Form
	{
		#region Initialization
		public FormComponentBinding(ServiceComponentSymbol component)
		{
			InitializeComponent();

			_component = component;
			_webServices = new List<OpenComposite.EII.Repository.WebService>();

			init();
		}
		public FormComponentBinding(ServiceComponentSymbol component, List<Repository.WebService> webservices)
		{
			InitializeComponent();

			_component = component;
			_webServices = webservices;

			init();
		}
		private void init()
		{
			_dicConnections = new Dictionary<ConnectionItem, OperationWebMethodConnection>();

			ucConnections.Connecting += new EventHandler<ConnectingEventArgs>(ucConnections_Connecting);
			ucConnections.Connected += new EventHandler<ConnectedEventArgs>(ucConnections_Connected);
			ucConnections.ConnectionsRemoved += new EventHandler<ConnectionItemsEventArgs>(ucConnections_ConnectionsRemoved);

			ucConnections.ContextMenuStrip.Items.Insert
				(0, new ToolStripMenuItem("Set Input Mapping (log->phys)...", null, this.miInputMapping_Click));
			ucConnections.ContextMenuStrip.Items.Insert
				(1, new ToolStripMenuItem("Set Output Mapping (phys->log)...", null, this.miOutputMapping_Click));
			ucConnections.ContextMenuStrip.Items.Insert
				(2, new ToolStripSeparator());

		}

		#endregion

		#region Public Members

		#region Properties
		public ServiceComponentSymbol Component
		{
			get { return _component; }
		}
		public List<Repository.WebService> WebServices
		{
			get { return _webServices; }
		}
		#endregion

		#endregion

		#region Private Members

		#region Variables
		private ServiceComponentSymbol _component = null;
		private List<Repository.WebService> _webServices = null;
		private Dictionary<ConnectionItem, OperationWebMethodConnection> _dicConnections;
		//private Dictionary<ConnectionItem, OperationWebMethodConnection> _dicRemovedConnections;
		#endregion

		#region Methods
		private void loadBindings()
		{
			// preconditions
			if ( _component == null )
				throw new NullReferenceException("Component undefined.");

			if ( _webServices == null )
				_webServices = new List<OpenComposite.EII.Repository.WebService>();

			// get connected webservices from database
			BindingListEx<Repository.WebService> listWS =
				Global.Data.GetServiceWebService(_component.RepositoryItem.ID);

			// add new webservices to _webService list
			foreach ( Repository.WebService ws1 in listWS ) {
				bool exists = false;
				foreach ( Repository.WebService ws2 in _webServices ) {
					if ( ws1 == ws2 ) {
						exists = true;
						break;
					}
				}
				if ( !exists )
					_webServices.Add(ws1);
			}
		}
		private void saveBindings()
		{
			List<Repository.WebService> listWS = new List<OpenComposite.EII.Repository.WebService>();
			// save connections to webservices and webmethods
			foreach ( OperationWebMethodConnection con in _dicConnections.Values ) {
				if ( con.NotInitialized )
					continue;

				TreeNode tnOp = con.ConnectionItem.LeftTreeNode;
				TreeNode tnWM = con.ConnectionItem.RightTreeNode;
				Repository.Operation op = ucConnections.GetNodeTag(tnOp) as Repository.Operation;
				Repository.WebMethod wm = ucConnections.GetNodeTag(tnWM) as Repository.WebMethod;
				Repository.WebService ws = ucConnections.GetNodeTag(tnWM.Parent) as Repository.WebService;
				if ( !listWS.Contains(ws) )
					listWS.Add(ws);

				Global.Data.DeleteOperationWebMethods(op.ID);
				Global.Data.SetOperationWebMethod(op.ID, wm.ID, con.InputConnections, con.InputXslt,
					con.OutputConnections, con.OutputXslt);
			}
			foreach ( Repository.WebService ws in listWS ) {
				Global.Data.SetServiceWebService(_component.RepositoryItem.ID, ws.ID);
			}
		}
		private void showMapping(bool isInput)
		{
			if ( ucConnections.SelectedConnections.Count == 0 ) {
				MessageBox.Show("Select a connection.", "Mapping", MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			}
			if ( !_dicConnections.ContainsKey(ucConnections.SelectedConnections[0]) )
				return;

			bool isHumanAct = false;
			OperationWebMethodConnection con = _dicConnections[ucConnections.SelectedConnections[0]];

			TreeNode tnOp = con.ConnectionItem.LeftTreeNode;
			TreeNode tnWM = con.ConnectionItem.RightTreeNode;
			Repository.Operation op = ucConnections.GetNodeTag(tnOp) as Repository.Operation;
			Repository.WebMethod wm = ucConnections.GetNodeTag(tnWM) as Repository.WebMethod;
			Repository.WebMethod wmHR = null;
			// special mapping for human activities
			if ( tnWM.Text == "SetInputData" ) {
				foreach ( TreeNode tnHR in tnWM.Parent.Nodes ) {
					if ( tnHR.Text == "HumanResponse" ) {
						tnWM = tnHR;
						wmHR = ucConnections.GetNodeTag(tnWM) as Repository.WebMethod;
						isHumanAct = true;
						break;
					}
				}
			}

			FormMapping frm = new FormMapping();
			if ( con.NotInitialized ) {
				loadOperationWebMethodConnection(op, wm, con);
			}
			if ( isInput ) {
				string xslt = con.InputXslt == null ? null : con.InputXslt.InnerXml;
				string mapping = con.InputConnections == null ? null : con.InputConnections.DocumentElement.OuterXml;
				frm.InitializeMapper(op.InputSchema, wm.InputSchema, xslt, mapping, null);
			} else {
				if ( isHumanAct ) {
					string xslt = con.OutputXslt == null ? null : con.OutputXslt.InnerXml;
					string mapping = con.OutputConnections == null ? null : con.OutputConnections.DocumentElement.OuterXml;
					frm.InitializeMapper(wmHR.InputSchema, op.OutputSchema, xslt, mapping, null);
				} else {
					string xslt = con.OutputXslt == null ? null : con.OutputXslt.InnerXml;
					string mapping = con.OutputConnections == null ? null : con.OutputConnections.DocumentElement.OuterXml;
					frm.InitializeMapper(wm.OutputSchema, op.OutputSchema, xslt, mapping, null);
				}
			}
			if ( frm.ShowDialog() == DialogResult.OK ) {
				string mapping;
				XsltDocument xslt = frm.GetMapping(out mapping);
				if ( isInput ) {
					con.InputConnections = new XmlDocument();
					con.InputConnections.LoadXml(mapping);
					con.InputXslt = xslt;
				} else {
					con.OutputConnections = new XmlDocument();
					con.OutputConnections.LoadXml(mapping);
					con.OutputXslt = xslt;
				}
			}
		}

		private void setInputMappingPhysical()
		{
			if ( ucConnections.SelectedConnections.Count == 0 ) {
				MessageBox.Show("Select a connection.", "Mapping", MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			}
			if ( !_dicConnections.ContainsKey(ucConnections.SelectedConnections[0]) )
				return;

			//bool isHumanAct = false;
			OperationWebMethodConnection con = _dicConnections[ucConnections.SelectedConnections[0]];

			TreeNode tnOp = con.ConnectionItem.LeftTreeNode;
			TreeNode tnWM = con.ConnectionItem.RightTreeNode;
			Repository.Operation op = ucConnections.GetNodeTag(tnOp) as Repository.Operation;
			Repository.WebMethod wm = ucConnections.GetNodeTag(tnWM) as Repository.WebMethod;
			//Repository.WebMethod wmHR = null;
			//// special mapping for human activities
			//if ( tnWM.Text == "SetInputData" ) {
			//    foreach ( TreeNode tnHR in tnWM.Parent.Nodes ) {
			//        if ( tnHR.Text == "HumanResponse" ) {
			//            tnWM = tnHR;
			//            wmHR = ucConnections.GetNodeTag(tnWM) as Repository.WebMethod;
			//            isHumanAct = true;
			//            break;
			//        }
			//    }
			//}
			Repository.LogicalActivity logact = null;
			BindingListEx<Repository.LogicalActivity> logacts = op.GetLogicalActivities();
			if ( logacts.Count == 0 ) {
				MessageBox.Show("No Logical Activity connected.", "Physical Input Mapping", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			} else if ( logacts.Count == 1 ) {
				logact = logacts[0];
			} else {
				FormSelectRepositoryItems frmSel = new FormSelectRepositoryItems();
				frmSel.MultiSelect = false;
				frmSel.SetItems(logacts.ConvertAll<Repository.RepositoryItemBase>(
					delegate(Repository.LogicalActivity la) { return (Repository.RepositoryItemBase)la; }));
				if ( frmSel.ShowDialog() == DialogResult.OK && frmSel.SelectedItems.Count > 0 ) {
					logact = frmSel.SelectedItems[0] as Repository.LogicalActivity;
				}
			}
			if ( logact == null )
				return;

			NamedBusinessObjectCollection lstSubscriptions = logact.GetSubscribedBusinessObjects();
			if ( lstSubscriptions.Count == 0 ) {
				MessageBox.Show("No subscribed documents available.", "Physical Input Mapping", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}
			List<XmlSchema> schemas = new List<XmlSchema>();
			foreach ( Repository.BusinessObject bo in lstSubscriptions.BusinessObjects ) {
				schemas.Add(bo.Schema);
			}

			XmlSchema xsdSubscriptions = Helpers.CreateCompoundCanonicalSchema(schemas.ToArray());

			FormMapping frm = new FormMapping();
			frm.InitializeMapper(xsdSubscriptions, wm.InputSchema,
				logact.PhysicalInputXslt, logact.PhysicalInputConnections, logact.ExtendedProperties.InputCodeMapping);
			if ( frm.ShowDialog() == DialogResult.OK ) {
				string map;
				XsltDocument xslt = frm.GetMapping(out map);
				if ( xslt != null && xslt.DocumentElement != null ) {

					logact.PhysicalInputConnections = map;
					logact.PhysicalInputXslt = xslt.DocumentElement.OuterXml;
					logact.Save();
					con.InputConnections = new XmlDocument();
					con.InputConnections.LoadXml(map);
					con.InputXslt = xslt;
				}
			}
		}

		private void loadOperationWebMethodConnection
			(Repository.Operation op, Repository.WebMethod wm, OperationWebMethodConnection con)
		{
			Global.Data.GetOperationWebMethodDetails(op.ID, wm.ID,
				out con.InputConnections, out con.InputXslt,
				out con.OutputConnections, out con.OutputXslt);
		}

		private void fillTrees()
		{
			// preconditions
			if ( _component == null )
				throw new NullReferenceException("Component undefined.");

			// clear treeviews
			ucConnections.LeftTreeView.Nodes.Clear();
			ucConnections.RightTreeView.Nodes.Clear();

			// add component to left treeview
			TreeNode tnComponent = ucConnections.LeftTreeView.Nodes.Add(_component.Name);
			ucConnections.SetNodeTag(tnComponent, _component);

			// add component operations to component node
			foreach ( InterfaceSymbol isymbol in _component.Interfaces ) {
				foreach ( Repository.Operation op in isymbol.Operations ) {
					TreeNode tnOp = tnComponent.Nodes.Add(op.Name);
					ucConnections.SetNodeTag(tnOp, op);
				}
			}

			// add webservices to right treeview
			if ( _webServices != null ) {
				foreach ( Repository.WebService websvc in _webServices ) {
					// create webservice treenode
					TreeNode tnWS = ucConnections.RightTreeView.Nodes.Add(websvc.Name);
					ucConnections.SetNodeTag(tnWS, websvc);

					// get webmethod list
					BindingListEx<Repository.WebMethod> listWM =
						Global.Data.GetRepositoryHierarchyChildRepositoryList<Repository.WebMethod>
							(Repository.ItemType.WebService, websvc.ID, Repository.ItemType.WebMethod);

					// add webmethods to webservice node
					foreach ( Repository.WebMethod webmeth in listWM ) {
						TreeNode tnWM = tnWS.Nodes.Add(webmeth.Name);
						ucConnections.SetNodeTag(tnWM, webmeth);
					}
				}
			}
			// load connections
			Repository.Service svc = _component.RepositoryItem as Repository.Service;
			if ( svc != null ) {
				ucConnections.SetConnectionXml(svc.WebServiceConnections);
			}

			ucConnections.LeftTreeView.ExpandAll();
			ucConnections.RightTreeView.ExpandAll();

			TreeNode tnLeft = null;
			TreeNode tnRight = null;
			if ( ucConnections.LeftTreeView.Nodes.Count == 1 &&
				 ucConnections.LeftTreeView.Nodes[0].Nodes.Count == 1 ) {
				tnLeft = ucConnections.LeftTreeView.Nodes[0].Nodes[0];
			}
			if ( ucConnections.RightTreeView.Nodes.Count == 1 &&
				 ucConnections.RightTreeView.Nodes[0].Nodes.Count == 1 ) {
				tnRight = ucConnections.RightTreeView.Nodes[0].Nodes[0];
			}
			if ( tnLeft != null && tnRight != null && ucConnections.Connections.Count == 0 ) {
				ucConnections.AddConnection(tnLeft, tnRight, ConnectDirection.LeftToRight);
			}
		}

		private void saveClose()
		{
			// save connection xml to service
			Repository.Service svc = _component.RepositoryItem as Repository.Service;
			if ( svc != null ) {
				svc.WebServiceConnections = ucConnections.GetConnectionXml();
				svc.Save();
			}

			// save connections to db
			saveBindings();

			// close form
			this.DialogResult = DialogResult.OK;
			this.Close();
		}

		private void cancel()
		{
			this.DialogResult = DialogResult.Cancel;
			this.Close();
		}
		#endregion

		#region Event Handler
		private void FormComponentBinding_Load(object sender, EventArgs e)
		{
			if ( !this.DesignMode ) {
				loadBindings();
				fillTrees();
			}
		}

		private void tsbtnSaveClose_Click(object sender, EventArgs e)
		{
			saveClose();
		}

		private void tsbtnCancel_Click(object sender, EventArgs e)
		{
			cancel();
		}

		private void miInputMapping_Click(object sender, EventArgs e)
		{
			//showMapping(true);
			setInputMappingPhysical();
		}
		private void miOutputMapping_Click(object sender, EventArgs e)
		{
			showMapping(false);
		}

		private void ucConnections_Connecting(object sender, ConnectingEventArgs e)
		{
			Repository.Operation op = ucConnections.GetNodeTag(e.Item.LeftTreeNode) as Repository.Operation;
			Repository.WebMethod wm = ucConnections.GetNodeTag(e.Item.RightTreeNode) as Repository.WebMethod;

			if ( op == null || wm == null )
				e.Cancel = true;
		}
		private void ucConnections_Connected(object sender, ConnectedEventArgs e)
		{
			_dicConnections.Add
				(e.Item, new OperationWebMethodConnection(e.Item, null, null, null, null));
		}

		private void ucConnections_ConnectionsRemoved(object sender, ConnectionItemsEventArgs e)
		{
			foreach ( ConnectionItem item in e.Items ) {
				if ( _dicConnections.ContainsKey(item) )
					_dicConnections.Remove(item);
			}
		}

		#endregion

		#endregion

	}

	#region Class: OperationWebMethodConnection
	public class OperationWebMethodConnection
	{
		public OperationWebMethodConnection()
		{
			ConnectionItem = null;
			InputConnections = null;
			InputXslt = null;
			OutputConnections = null;
			OutputXslt = null;
		}
		public OperationWebMethodConnection(
			ConnectionItem connectionItem,
			XmlDocument inputConnections, XsltDocument inputXslt,
			XmlDocument outputConnections, XsltDocument outputXslt)
		{
			ConnectionItem = connectionItem;
			InputConnections = inputConnections;
			InputXslt = inputXslt;
			OutputConnections = outputConnections;
			OutputXslt = outputXslt;
		}

		public ConnectionItem ConnectionItem;
		public XmlDocument InputConnections;
		public XsltDocument InputXslt;
		public XmlDocument OutputConnections;
		public XsltDocument OutputXslt;

		public bool NotInitialized
		{
			get
			{
				return ( this.ConnectionItem == null &&
						 this.InputConnections == null && this.InputXslt == null &&
						 this.OutputConnections == null && this.OutputXslt == null );
			}
		}
	}
	#endregion
}
